#include <iostream>
#include <fstream>
#include <omp.h>
#include <codecvt>
#include <locale>
#include <string>

#include "Grid.hpp"
#include "Func.hpp"

using namespace std;
using datatype = double;
using PointType = GridPoint<datatype>;
using IndexType = int;

int main()
{  
    /* Please set your parameters */
    const std::string GridName = "GridB";
    const datatype lc = 20.0;
    const datatype xExtensionFactor = 0.1;
    const datatype yExtensionFactor = 0.1;
    const datatype zExtensionUpper = 1050, zExtensionLower = 400;
    
    CornerGrid<false, datatype, GridCell<PointType, datatype, 8>> basegrid(GridName);
    CornerGrid<true, datatype, GridCell<PointType, datatype, 8>> finegrid(GridName);
    FinElemGrid<GridCell<IndexType, datatype, 4>, datatype, 4> finelemgrid(GridName);
    std::vector<std::pair<datatype, datatype>> finegrid_prefixZ;
    std::vector<std::vector<std::pair<datatype, datatype>> > finegrid_prefixY;
       
    const std::string datapos = "../../../../../";
    const std::string basegrid_file = datapos + "data/" + GridName + "/baseGrid.out";
    const std::string finegrid_file = datapos + "data/" + GridName + "/fineGrid.out";
    const std::string prop_file = datapos + "data/" + GridName + "/Props.txt";
    const std::string fault_file = datapos + "data/" + GridName + "/faults.txt";
    const std::string output_file = datapos + "data/output/output_" + GridName + ".vtk";

    double io_time = 0.0, remesh_time = 0.0, map_time = 0.0;

    cout << "======== Begin " << GridName << " Conversion ========" << endl;std::cout.flush();
    
    io_time -= omp_get_wtime();
    {
        basegrid.LoadGrid(basegrid_file);
        finegrid.LoadGrid(finegrid_file, prop_file);
        basegrid.LoadFault(fault_file);
    }
    io_time += omp_get_wtime();

    cout << "Base Grid Name: " << basegrid.GetGridName() << endl;
    cout << "Base Grid Size: " << basegrid.GetNx() << " x " << basegrid.GetNy() << " x " << basegrid.GetNz() << endl;
    cout << "Fine Grid Size: " << finegrid.GetNx() << " x " << finegrid.GetNy() << " x " << finegrid.GetNz() << endl;

    /* Grid Remeshing */
    remesh_time -= omp_get_wtime();
    {
        GridRemesh<datatype>(basegrid, finelemgrid, lc, xExtensionFactor, yExtensionFactor, zExtensionUpper, zExtensionLower);
    }
    remesh_time += omp_get_wtime();
    
    /* Grid property Mapping */
    map_time -= omp_get_wtime();
    {
        ComputePrefix<datatype>(finegrid, finegrid.GetNx(), finegrid.GetNy(), finegrid.GetNz(), finegrid_prefixZ, finegrid_prefixY);
        PropMapping<datatype>(finegrid, finegrid_prefixZ, finegrid_prefixY, finelemgrid);
    }
    map_time += omp_get_wtime();

    /* Output Grid */
    io_time -= omp_get_wtime();
    {
        finelemgrid.WriteToVtk(output_file);
    }

    io_time += omp_get_wtime();

    cout << "======== " << GridName << " Conversion Time Stat ========" << endl;
    cout << "IO Time: " << io_time << " seconds" << endl;
    cout << "Remesh Time: " << remesh_time << " seconds" << endl;
    cout << "Mapping Time: " << map_time << " seconds" << endl;

    return 0;
}